import os
import urlparse
import posixpath
import sqlite3
import BaseHTTPServer
import cgi
import urllib
import base64
import builderPie.demjson as json
import builderPie.serverFunctions
import SocketServer

serverFunctions=builderPie.serverFunctions

conn=sqlite3.connect('../data/build_info.db')
conn.row_factory=sqlite3.Row

mimeTypes={}
mimeTypes['jpg']='image/jpeg'
mimeTypes['jpeg']='image/jpeg'
mimeTypes['jpe']='image/jpeg'
mimeTypes['gif']='image/gif'
mimeTypes['png']='image/png'

class HTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        response=0
        headers=[]
        if self.path.find('?')==-1:
            (content, contentType, headers, response)=self.serveStats()
        else:
            #get querystring and make the JSON
            try:
                rawQuerystring=self.path.split('?')
                
                querystring=cgi.parse_qs(rawQuerystring)
                method=querystring['method']
                params=base64.decodestring(urllib.unquote(querystring['params']))
                id=querystring['id']
                
                rawJSON='{"method": "%s", "params": %s, "id": %s}' % (method, params, id)
            except:
                rawJSON=""
        
            content=self.doRPC(rawJSON)
            contentType='text/plain'
        
        if not response: self.send_response(200)
        else: self.send_response(response)
        
        if len(headers)>0:
            for header in headers:
                self.send_header(*header)
        
        self.send_header("Content-type", contentType)
        self.send_header("Content-Length", len(content))
        self.end_headers()

        #output file object
        self.wfile.write(content)
    
    def do_POST(self):
        #get the post data
        length=int(self.headers.getheader('content-length'))
        rawPost=self.rfile.read(length)
        
        content=self.doRPC(rawPost)

        #make http response
        self.send_response(200)
        self.send_header("Content-type", 'text/plain')
        self.send_header("Content-Length", len(content))
        self.end_headers()
        
        #output file object
        self.wfile.write(content)
    
    def doRPC(self, rawJSON):
        #decode the json-rpc message
        try:
            rpc=json.decode(rawJSON, strict=True)
        except JSONDecodeError:
            rpc={"id": "null", "params": "null", "method": ""}
            callFunction=lambda _: ("null", '{"code": -32700, "message": "Parse error."}')
            
        #test the rpc object to be sure it is the correct form
        if self.badJSONRPC(rpc):
            rpc={"id": "null", "params": "null", "method": ""}
            callFunction=lambda _: ("null", '{"code": -32600, "message": "Invalid JSON-RPC."}')
            
        #only respond to path '/'
        if self.path!='/':
            rpc={"id": rpc['id'], "params": "null", "method": ""}
            callFunction=lambda _: ("null", '{"code": -32000, "message": "Path must be \'/\'."}')

        #get the function from the rpc 'method'
        if rpc['method']!="":
            try:
                callFunction=getattr(serverFunctions, rpc['method'])
            except:
                callFunction=lambda _: ("null", '{"code": -32601, "message": "Procedure not found."}')
                
        #call the function
        (result, error)=callFunction(rpc)
        rpcMsg={"result": result, "error": error, "id": rpc['id']}
        
        return json.encode(rpcMsg)

    def badJSONRPC(self, jsonRPC):
        if len(jsonRPC)!=3: return True
        
        try:
            _=jsonRPC['method']
            _=jsonRPC['params']
            _=jsonRPC['id']
        except KeyError:
            return True
        
        return False
        
    def log_message(self, format, *args):
        #do nothing
        return ""
        
    def translate_path(self, path):
        """Translate a /-separated PATH to the local filename syntax.

        Components that mean special things to the local file system
        (e.g. drive or directory names) are ignored.  (XXX They should
        probably be diagnosed.)

        """
        
        # abandon query parameters
        path = urlparse.urlparse(path)[2]
        path = posixpath.normpath(urllib.unquote(path))
        words = path.split('/')
        words = filter(None, words)
        path = os.getcwd()
        path = os.path.join(path, '..', 'data', 'stats')
        path = os.path.abspath(path)
        for word in words:
            drive, word = os.path.splitdrive(word)
            head, word = os.path.split(word)
            if word in (os.curdir, os.pardir): continue
            path = os.path.join(path, word)
        return path

    def serveStats(self):
        headers=[]
        response=200
        
        if self.path=='/favicon.ico':
            (content, type) = self.serveFavicon()
            return (content, type, headers, response)
        
        if self.path.startswith('/css/'):
            (content, type) = self.serveCSS()
            return (content, type, headers, response)
        
        if self.path.startswith('/images/'):
            (content, type) = self.serveImage()
            return (content, type, headers, response)
        
        reload(builderPie.serverFunctions)
        fns=builderPie.serverFunctions

        if self.path=='/':
            (html, headers, response) = fns.serveRoot()
        
        if self.path.startswith('/details/'):
            html = fns.serveDetails(self.path)
        
        if self.path.startswith('/builds/'): 
            html = fns.serveBuildStats(self.path)
        
        fp=open('../data/stats/index.tmpl')
        
        content=fp.read()
        type='text/html'
        fp.close()
        
        content=content.replace('%stats%', html)
        
        return (content, type, headers, response)

    def serveFavicon(self):
        path=os.path.join(os.path.abspath('..'), 'data','stats', 'favicon.ico')
        fp=open(path, 'rb')
        
        type='image/vnd.microsoft.icon'
        content=fp.read()
        fp.close()

        return content, type
        
    def serveCSS(self):
        path = self.translate_path(self.path)
        fp=open(path)
        
        type='text/css'
        content=fp.read()
        fp.close()

        return content, type
        
    def serveImage(self):
        path = self.translate_path(self.path)
        (_,ext) = path.split('.')
        fp=open(path, 'rb')
        
        type=mimeTypes[ext]
        content=fp.read()
        fp.close()
        
        return content, type

def serve(interface, port):
    server=HTTPRequestHandler
    socket=SocketServer.TCPServer((interface, port), server)

    socket.serve_forever()
